package pers.rcl.common.component;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.TypeReference;
import org.apache.commons.lang3.StringUtils;
import org.redisson.api.RAtomicLong;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;
import pers.rcl.common.util.LocalDateUtils;

import javax.annotation.Resource;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;

@Component
public class RedisUtil {

    @Resource
    private StringRedisTemplate stringRedisTemplate;

    @Resource
    private RedissonClient redissonClient;

    @Value("${spring.redis.prifix}")
    private String keyPrefix;

    private static final Map<String, RAtomicLong> R_ATOMIC_LONG_MAP = new ConcurrentHashMap<>(16);

    public Optional<String> get(String key) {
        String value = stringRedisTemplate.opsForValue().get(key);
        return Optional.ofNullable(value);
    }

    public <T> Optional<T> get(String key, Class<T> clazz) {
        String value = stringRedisTemplate.opsForValue().get(key);
        T obj = null;
        if (StringUtils.isNotBlank(value)) {
            obj = JSON.parseObject(value, clazz);
        }
        return Optional.ofNullable(obj);
    }
    /**
     * 获取指定Type的值
     *
     * @param key  String
     * @param type TypeReference<T>
     * @return Optional<T>
     */
    public <T> Optional<T> getValue(String key, TypeReference<T> type) {
        String value = stringRedisTemplate.opsForValue().get(key);
        if (StringUtils.isBlank(value)) {
            return Optional.empty();
        }
        return Optional.ofNullable(JSONObject.parseObject(value, type));
    }
    public void set(String key, Object value) {
        if (value instanceof String) {
            stringRedisTemplate.opsForValue().set(key, value.toString());
        } else {
            stringRedisTemplate.opsForValue().set(key, JSON.toJSONString(value));
        }
    }

    public void set(String key, Object value, long timeout, TimeUnit unit) {
        if (value instanceof String) {
            stringRedisTemplate.opsForValue().set(key, value.toString(), timeout, unit);
        } else {
            stringRedisTemplate.opsForValue().set(key, JSON.toJSONString(value), timeout, unit);
        }
    }

    /**
     * 向一张hash表中放入数据,如果不存在将创建
     *
     * @param key     String
     * @param itemKey String
     * @param value   Object
     */
    public void hSet(String key, String itemKey, Object value) {
        if (value instanceof String) {
            stringRedisTemplate.opsForHash().put(key, itemKey, value.toString());
        } else {
            stringRedisTemplate.opsForHash().put(key, itemKey, JSONObject.toJSONString(value));
        }
    }

    public void delete(String key) {
        stringRedisTemplate.delete(key);
    }

    public Long increment(String key) {
        return stringRedisTemplate.opsForValue().increment(key, 1);
    }

    public RAtomicLong getAtomicLong(String key) {
        return redissonClient.getAtomicLong(keyPrefix + key);
    }

    /**
     * 单日统计次数（自增长）
     * @param key
     * @return
     */
    public RAtomicLong getAtomicLongWithOneDay(String key) {
        RAtomicLong atomicLong = R_ATOMIC_LONG_MAP.get(key);
        if (atomicLong != null) {
            if (!atomicLong.isExists()) {
                atomicLong.incrementAndGet();
                LocalDateTime todayEnd = LocalDateTime.of(LocalDate.now(), LocalTime.MAX);
                atomicLong.expireAt(LocalDateUtils.getTimeStamp(todayEnd));
            }
            return atomicLong;
        }
        atomicLong = redissonClient.getAtomicLong(keyPrefix + key);
        if (!atomicLong.isExists()) {
            atomicLong.incrementAndGet();
            LocalDateTime todayEnd = LocalDateTime.of(LocalDate.now(), LocalTime.MAX);
            atomicLong.expireAt(LocalDateUtils.getTimeStamp(todayEnd));
        }
        R_ATOMIC_LONG_MAP.put(key, atomicLong);
        return atomicLong;
    }
}
